package javango.forms.fields;

import java.lang.annotation.Annotation;
import java.util.Map;

import javango.forms.widgets.TextInputWidget;
import javango.forms.widgets.Widget;
import javango.forms.widgets.WidgetFactory;

import org.apache.commons.lang.StringUtils;

import com.google.inject.Inject;

public class FieldBase implements Field {

	protected Object initial;
	protected String name;
	protected String verboseName;
	protected String helpText;
	
	protected WidgetFactory widgetFactory;
	protected Class<? extends Widget> widgetClass; 
	protected Widget widget;
	protected Class<? extends Widget> hiddenWidgetClass;
	protected Widget hiddenWidget;
	
	private boolean required = true;
	private boolean hidden = false;
	private boolean editable = true;
	private boolean allowNull = true; // TODO This is not incorporated in all fiel types
	
	@Inject
	public FieldBase(WidgetFactory widgetFactory) {
		super();
		this.widgetFactory = widgetFactory;
	}

	// TODO should the map returned by clean be <String, String[]> so a field can have multiple errors?
	protected Object clean(String value, Map<String, String> errors) {
		if (StringUtils.isEmpty(value)) {
			if (isRequired()) { 
				errors.put(getName(), "Required");
			} else if (isAllowNull()) {
				return null;
			}
		}
		return value;
	}
	
	public Object clean(String[] value, Map<String, String> errors) {
		// TODO Should value == null,  value.length==0 be handled differently
		if (value == null || value.length == 0) {
			return clean("", errors);
		}
		return clean(value[0], errors);
	}
	
	public void handleAnnotation(Annotation annotation) {
		if (!(annotation instanceof FieldProperties)) return;
		FieldProperties props = (FieldProperties)annotation;
		this.setRequired(props.required());
		this.setEditable(props.editable());
		this.setAllowNull(props.allowNull());
		this.verboseName = props.verboseName();
		this.helpText = props.helpText();
		this.hidden = props.hidden();
	}
		
	public FieldBase setName(String name) {
		this.name = name;
		return this;
	}
	
	public String getName() {
		return this.name;
	}

	public Widget getWidget() {
		if (this.widget == null) {
			if (this.widgetClass == null) {
				this.widget = new TextInputWidget();
			} else {
				this.widget = widgetFactory.newWidget(widgetClass);
			}
		}
		Map<String, String> widgetAttrs = getWidgetAttrs();
		if (widgetAttrs != null) this.widget.getAttrs().putAll(getWidgetAttrs());
		
		return this.widget;
	}

	public Field setWidget(Widget widget) {
		this.widget = widget;
		return this;
	}

	public boolean isRequired() {
		return required;
	}
	public FieldBase setRequired(boolean required) {
		this.required = required;
		return this;
	}

	public Object getInitial() {
		return initial;
	}

	public FieldBase setInitial(Object initial) {
		this.initial = initial;
		return this;
	}

	public String getVerboseName() {
		return verboseName;
	}

	public FieldBase setVerboseName(String verboseName) {
		this.verboseName = verboseName;
		return this;
	}	
	
	public Widget getHiddenWidget() {
		if (this.hiddenWidget == null) {
			this.hiddenWidget = new TextInputWidget().setHidden(true);
		}
		return this.hiddenWidget;		
	}

	public FieldBase setHiddenWidget(Widget hiddenWidget) {
		this.hiddenWidget = hiddenWidget;
		return this;
	}
	
	public boolean isHidden() {
		return hidden;
	}

	public FieldBase setHidden(boolean hidden) {
		this.hidden = hidden;
		return this;
	}

	public boolean isEditable() {
		return editable;
	}

	public Field setEditable(boolean editable) {
		this.editable = editable;
		return this;
	}

	public boolean isAllowNull() {
		return allowNull;
	}

	public Field setAllowNull(boolean allowNull) {
		this.allowNull = allowNull;
		return this;
	}

	public String getHelpText() {
		return helpText;
	}

	public Field setHelpText(String helpText) {
		this.helpText = helpText;
		return this;
	}

	public Class<? extends Widget> getHiddenWidgetClass() {
		return hiddenWidgetClass;
	}

	public Class<? extends Widget> getWidgetClass() {
		return widgetClass;
	}

	public Field setHiddenWidget(Class<? extends Widget> widgetClass) {
		this.hiddenWidgetClass = widgetClass;
		return this;
	}

	public Field setWidget(Class<? extends Widget> widgetClass) {
		this.widgetClass = widgetClass;
		return this;
	}
	
	public Map<String, String> getWidgetAttrs() {
		return null;
	}

}
